# written by me (who never programs in ruby, except this one time) roughly
# based on the beanstalkd ruby module. <http://xph.us/software/beanstalkd/>
# if there is something that would help this module, please let me know - Grant


require 'socket'
require 'fcntl'

module ElementDB
  class Client
    attr_reader :addr
    def initialize(addr)
      @addr = addr
      connect
    end
  
    def connect
      host, port = addr.split(':')
      @socket = TCPSocket.new(host, port.to_i)
    
      @socket.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)    
    end
  
    def close
      @socket.close
      @socket = nil
    end

    def query( preamble, items=nil )
      # items should be an array of hashes
      if items == nil
        @socket.write("#{preamble}\r\n0\r\n")
        return response
      end
    
      q = "#{preamble}\r\n#{items.length}\r\n"
      items.each do |item|
        item_parts = []
        item.each_pair do |key, value|
          item_parts += ["#{key} #{value.length}"]
        end
        q += item_parts.join(",") + "\r\n"

        item.each_pair do |key, value|
          q += "#{value}\r\n"
        end
      end
      @socket.write(q)
    
      return response
    end
  
    
    def response
      result_pre = get_resp()
#      puts "result preamble is: #{result_pre}"
      icount = get_resp().to_i
#      puts "need to read #{icount} items"
      all_items = []
      icount.times { all_items += [get_item()] }
      return all_items
    end
    
    def get_item()
      attrs = {}
      item_hdr = get_resp()
      item_attrs = item_hdr.split(",")
      item_attrs.each do |a|
#        print("attribute: #{a}\n")
        key_name, value_sz = a.split(" ")
        value_sz = value_sz.to_i
        attrs[key_name] = read_bytes(value_sz)
        raise 'bad trailer' if read_bytes(2) != "\r\n"
      end
      attrs
    end
    
    def get_resp()
      r = @socket.gets("\r\n")
      raise EOFError if r == nil
      r[0...-2]
    end  
    
    def read_bytes(n)
      str = @socket.read(n)
      raise EOFError, 'End of file reached' if str == nil
      raise EOFError, 'End of file reached' if str.size < n
      str
    end  
  
  end
end


